Skip to content

10 typing类型注解

Python是动态类型语言,变量不需要声明类型。这很灵活,但代码一大就容易出问题——函数参数传错了类型、返回值类型和预期不符,这些错误只能在运行时发现。

typing模块给Python加上了类型注解(Type Hints),让IDE能提前发现类型错误,代码也更容易理解。类型注解不会影响运行时行为,只是给开发者和工具看的。

一、基本类型注解

1.1 变量注解

python
# 变量类型注解
name: str = "大志"
age: int = 28
score: float = 95.5
active: bool = True

1.2 函数参数和返回值

python
# 函数类型注解
def greet(name: str) -> str:
    return f"你好, {name}!"

def add(a: int, b: int) -> int:
    return a + b

# 无返回值用None
def log(message: str) -> None:
    print(message)

1.3 容器类型

python
# 列表
scores: list[int] = [90, 85, 92]

# 字典
user: dict[str, str] = {"name": "大志", "email": "test@example.com"}

# 集合
tags: set[str] = {"python", "ai"}

# 元组
point: tuple[int, int] = (10, 20)

Python 3.9+可以直接用内置类型(如list[int]),不需要从typing导入List

二、typing模块的核心类型

2.1 Any

任意类型,不做类型检查。

python
from typing import Any

def process(data: Any) -> None:
    # data可以是任何类型
    print(data)

尽量少用Any,它等于关闭了类型检查。

2.2 Union

联合类型,表示可以是多种类型之一。

python
from typing import Union

# Python 3.10+ 可以用 | 语法
def process(value: int | str) -> str:
    return str(value)

# 旧写法
def process(value: Union[int, str]) -> str:
    return str(value)

2.3 Optional

可选类型,表示可以是指定类型或None

python
from typing import Optional

# Optional[str] 等价于 Union[str, None]
def find_user(user_id: int) -> Optional[str]:
    if user_id == 1:
        return "大志"
    return None

# Python 3.10+ 写法
def find_user(user_id: int) -> str | None:
    ...

2.4 Callable

可调用对象类型(函数、方法等)。

python
from typing import Callable

# Callable[[参数类型], 返回类型]
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
    return func(a, b)

def add(a: int, b: int) -> int:
    return a + b

apply(add, 1, 2)  # 3

# 任意参数
def run(func: Callable[..., None]) -> None:
    func()

2.5 Literal

字面量类型,限定值只能是几个固定的选项。

python
from typing import Literal

def set_mode(mode: Literal["fast", "slow", "auto"]) -> None:
    print(f"模式: {mode}")

set_mode("fast")   # OK
set_mode("medium") # 类型检查器会报错

2.6 TypeVar

类型变量,用于泛型。

python
from typing import TypeVar

T = TypeVar('T')

def first(items: list[T]) -> T:
    return items[0]

# 返回类型会根据输入自动推断
result = first([1, 2, 3])  # result的类型是int
result = first(["a", "b"])  # result的类型是str

2.7 Generic

泛型基类。

python
from typing import Generic, TypeVar

T = TypeVar('T')

class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []
    
    def push(self, item: T) -> None:
        self._items.append(item)
    
    def pop(self) -> T:
        return self._items.pop()

# 使用
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)

str_stack: Stack[str] = Stack()
str_stack.push("hello")

三、Python 3.10+的新语法

3.1 联合类型用 |

python
# 旧写法
from typing import Union
def process(value: Union[int, str]) -> None: ...

# 新写法(3.10+)
def process(value: int | str) -> None: ...

3.2 类型别名用type

python
# 旧写法
from typing import TypeAlias
Vector: TypeAlias = list[float]

# 新写法(3.12+)
type Vector = list[float]
type UserID = int

3.3 TypeVar简写

python
# 旧写法
from typing import TypeVar
T = TypeVar('T')
def first(items: list[T]) -> T: ...

# 新写法(3.12+)
def first[T](items: list[T]) -> T: ...

四、高级类型

4.1 TypedDict

带类型键的字典。

python
from typing import TypedDict

class UserDict(TypedDict):
    name: str
    age: int
    email: str

# 使用
user: UserDict = {
    "name": "大志",
    "age": 28,
    "email": "test@example.com"
}

# 缺少键或类型错误时,类型检查器会报错

4.2 Protocol

结构化子类型(鸭子类型的类型注解版本)。

python
from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None: ...

class Circle:
    def draw(self) -> None:
        print("画圆")

class Square:
    def draw(self) -> None:
        print("画方")

# Circle和Square都没有继承Drawable,但都有draw方法
def render(shape: Drawable) -> None:
    shape.draw()

render(Circle())  # OK
render(Square())  # OK

4.3 Final

标记不可重新赋值的常量。

python
from typing import Final

MAX_RETRIES: Final = 3
API_URL: Final = "https://api.example.com"

# 重新赋值时类型检查器会报错
MAX_RETRIES = 5  # 类型检查器报错

4.4 ClassVar

标记类变量(不参与实例的类型检查)。

python
from typing import ClassVar

class Agent:
    version: ClassVar[str] = "1.0"
    name: str

    def __init__(self, name: str) -> None:
        self.name = name

# version是类变量,所有实例共享
# name是实例变量,每个实例不同

4.5 Annotated

给类型添加额外元数据。

python
from typing import Annotated

# Annotated[类型, 元数据]
UserId = Annotated[int, "用户ID"]
Email = Annotated[str, "邮箱地址"]

def get_user(user_id: UserId) -> str:
    return "大志"

# 常用于FastAPI等框架的参数验证
# from fastapi import Query
# def search(q: Annotated[str, Query(min_length=1)]): ...

五、实际项目中的用法

5.1 API返回类型

python
from typing import TypedDict

class ApiResponse(TypedDict):
    code: int
    message: str
    data: dict | None

def fetch_user(user_id: int) -> ApiResponse:
    return {
        "code": 200,
        "message": "success",
        "data": {"name": "大志"}
    }

5.2 配置类型

python
from typing import TypedDict, Literal

class Config(TypedDict):
    host: str
    port: int
    debug: bool
    log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"]

def load_config() -> Config:
    return {
        "host": "localhost",
        "port": 8080,
        "debug": False,
        "log_level": "INFO"
    }

5.3 回调函数类型

python
from typing import Callable

EventHandler = Callable[[str, dict], None]

def on_event(handler: EventHandler) -> None:
    handler("click", {"x": 100, "y": 200})

def my_handler(event: str, data: dict) -> None:
    print(f"事件: {event}, 数据: {data}")

on_event(my_handler)

5.4 泛型容器

python
from typing import Generic, TypeVar, Iterator

T = TypeVar('T')

class Repository(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []
    
    def add(self, item: T) -> None:
        self._items.append(item)
    
    def get_all(self) -> list[T]:
        return self._items.copy()
    
    def find(self, predicate: Callable[[T], bool]) -> T | None:
        for item in self._items:
            if predicate(item):
                return item
        return None

# 使用
user_repo: Repository[User] = Repository()
user_repo.add(User("大志"))

六、运行时类型检查

类型注解默认不影响运行时,但可以用get_type_hints()获取:

python
from typing import get_type_hints

def greet(name: str) -> str:
    return f"你好, {name}!"

hints = get_type_hints(greet)
print(hints)  # {'name': <class 'str'>, 'return': <class 'str'>}

如果需要运行时检查,可以用typeguard库:

python
# pip install typeguard
from typeguard import typechecked

@typechecked
def add(a: int, b: int) -> int:
    return a + b

add(1, 2)      # OK
add("1", 2)    # TypeError

七、总结

typing模块的核心类型:

类型用途
Any任意类型
Union[X, Y]X | Y联合类型
Optional[X]X | None可选类型
Callable[[Args], Ret]可调用对象
Literal[...]字面量类型
TypeVar类型变量(泛型)
Generic泛型基类
TypedDict带类型的字典
Protocol结构化子类型
Final不可重新赋值
ClassVar类变量

使用建议:

  1. 函数签名必加:参数和返回值加上类型注解,IDE能提供更好的补全和检查
  2. 变量类型按需:复杂类型加注解,简单类型可以省略
  3. 用新语法:Python 3.10+用X | Y代替Union[X, Y],3.12+用type定义类型别名
  4. 不要过度使用:简单的代码不需要复杂的泛型,保持简洁

类型注解是Python从"能跑就行"走向"工程化"的重要一步。项目大了、团队协作时,类型注解的价值就体现出来了。